/*
 * Title:        CloudScheduler Toolkit
 * Description:  a Toolkit for Modeling and Simulation of Job Scheduling and Resource Provisioning in Cloud System
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 * Author:       Fuhui Wu
 *
 * Copyright (c) 2013-2014, The National University of Defense Technology, China
 */

package org.nudt.jCloud.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.nudt.jCloud.infrastructure.SlotType;
import org.nudt.jCloud.simulation.CloudSchedSIM;
import org.nudt.jCloud.simulation.Framework;

public class Log {
	
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	private static Map<String, OutputStream> output = new HashMap<String, OutputStream>();
	private static boolean disabled;
	private static DecimalFormat df_1 = new DecimalFormat("#0.00");
	private static DecimalFormat df_2 = new DecimalFormat("#0.0000000000");
	
	
	public static void init(){
		Set<String> keys = output.keySet();
		Iterator<String> keyItr = keys.iterator();
		while(keyItr.hasNext()){
			String key = keyItr.next();
			try {
				output.get(key).close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		output.clear();
		
		File file = new File("Log/"+CloudSchedSIM.name);
		if(file.exists()){
			try {
				delete(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void delete(File file) throws IOException{
		if(file.isDirectory()){
			if(file.list().length==0){
				file.delete();
			}else{
				String files[] = file.list();
				for (String temp : files) {
					File fileDelete = new File(file, temp);
					delete(fileDelete);
				}
				if(file.list().length==0){
					file.delete();
				}
			}
		}else{
			if(!file.delete()){
				System.out.println("File delete failed.");
			}    		
    	}
	}

	
	public static void print( String msg){
		if(!isDisabled()){
			try {
				getOutput("system_dot_out").write((df_1.format(CloudSchedSIM.getClock())+":"+msg).getBytes());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void println(String msg){
		print(msg+"\n");
	}
	
	public static void jobLog(String user, double timestamp, int XJobId, int state){
		try {
			getOutput(user+"_Job").write((df_2.format(timestamp)+"\t"+XJobId+"\t"+state+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 
	 * @param userBroker
	 * @param timestamp
	 * @param XJobId
	 * @param jobId
	 * @param taskId
	 * @param state
	 * @param hostId
	 * @param locality
	 */
	public static void taskLog(String user, double timestamp, int XJobId, int jobId, int taskId, 
			int state, int hostId, boolean locality){
		try {
			getOutput(user+"_Task").write((df_2.format(timestamp)+"\t"+Integer.toString(XJobId)+"_"
					+Integer.toString(jobId)+"_"+Integer.toString(taskId)+"\t"+state+"\t"+hostId+"\t"+(locality? 1:0)+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void jobSchedulerDelayLog(String userBroker, double timestamp, double delay){
		try {
			getOutput(userBroker+"_JobScheduler").write((df_2.format(timestamp)+"\t"+
					Double.toString(delay)+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void concurrentFailureLog(String userBroker, double timestamp, int failureCount){
		try {
			getOutput(userBroker+"_concurrentFailure").write((df_2.format(timestamp)+"\t"+
					Integer.toString(failureCount)+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 
	 * @param timestamp
	 * @param hostId
	 * @param state Keep(0), powerUp(1), powerDown(2);
	 * @param operation Lease a slot(0), release a slot(1);
	 * @param peNum The lease/release slot pe number;
	 * @param ram The lease/release slot ram.
	 */
	public static void hostLog(double timestamp, int hostId, int state, int operation, int peNum, int ram){
		try {
			getOutput("Host").write((df_2.format(timestamp)+"\t"+hostId+"\t"+state+"\t"+
					operation+"\t"+peNum+"\t"+ram+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void frwkLog(double timestamp, Framework frwk){
		String userBroker = frwk.getUserBroker().getName();
		OutputStream out = getOutput(userBroker+"_Frwk");
		int total = 0;
		try {
			out.write((df_2.format(timestamp)+";").getBytes());
			for(SlotType slotType : frwk.getFreeSlots().keySet()){
				int count = frwk.getFreeSlots().get(slotType).size();
				total = total +count;
				out.write((""+slotType.getPeCount()+";"+slotType.getRam()+";"
						+count).getBytes());
			}
			if(frwk.getFreeSlots().size()<=0){
				out.write(("0;0;0").getBytes());
			}
			out.write((";").getBytes());
			for(SlotType slotType : frwk.getBusySlots().keySet()){
				int count = frwk.getBusySlots().get(slotType).size();
				total = total+count;
				out.write((""+slotType.getPeCount()+";"+slotType.getRam()+";"
							+count).getBytes());
			}
			if(frwk.getBusySlots().size()<=0){
				out.write(("0;0;0").getBytes());
			}
			out.write((";"+total+"\n").getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @return the lineSeparator
	 */
	public static String getLineSeparator() {
		return LINE_SEPARATOR;
	}
	
	/**
	 * @return the output
	 */
	public static OutputStream getOutput(String file) {
		if(file=="system_dot_out"){
			return System.out;
		}else{
			try {
				File dir = new File("Log"+"/"+CloudSchedSIM.name);
				if(!dir.exists()){
					dir.mkdir();
				}
				File f = new File(dir.getAbsolutePath()+"/"+file);
				if(!f.exists()){
					try {
						if(!f.createNewFile()){
							throw new RuntimeException("Cannot create file.");
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if(output.containsKey(f.getName())){
					return output.get(f.getName());
				}else{
					FileOutputStream out = new FileOutputStream(f, true);
					output.put(f.getName(), out);
					return out;
				}
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		return null;
	}


	/**
	 * @return the disabled
	 */
	public static boolean isDisabled() {
		return disabled;
	}

	/**
	 * @param disabled the disabled to set
	 */
	public static void setDisabled(boolean disabled) {
		Log.disabled = disabled;
	}

	public static DecimalFormat getDf_2() {
		return df_2;
	}

	public static void setDf_2(DecimalFormat df_2) {
		Log.df_2 = df_2;
	}

}
